import * as koa from 'koa2';
import * as cors from 'koa2-cors'
import * as Router from 'koa-router';
import crypto from '../utils/crypto';
import http from '../utils/http';
import Configs_win from '../configs_win';
import db from '../utils/db';
import Http from '../utils/http';
import Db from '../utils/db';
import Crypto from '../utils/crypto';
import { httpRouterMap } from '../configs_win';





const callPth = 'hall_server -> client_service ->';
const app = new koa();
const router = new Router();
let config: hall_server = null;
const serverMap: {[key:string]:gameServerInfoType}= {};


// 设置跨域
app.use(cors());
// 设置路由
app.use(router.routes()).use(router.allowedMethods());





export default class Room_service{

    /**
     * 开启房间管理服务器
     * @param _config 
     */
    static start(_config: hall_server){
        config = _config;
        app.listen(config.ROOM_PORT, config.FOR_ROOM_IP, () => {
            console.log('房间管理服务器运行在' + config.CLIENT_PORT + '端口');
        });
    }

    /**
     * 创建房间
     * 
     */
    static async createRoom(account, userId, roomConf: conf): Promise<{ errcode?: number, errmsg?: string, roomId?}>{
        const serverInfo = chooseServer();
        if(serverInfo == null){
            return {errcode: 101, errmsg: callPth + 'createRoom->serverInfo为空'};
        }

        const gems = await db.get_gems(account);
        if(!gems){
            return {errcode: 103, errmsg: callPth + callPth + 'createRoom->gems为空'};
        }

        const reqData: ret = {
            userId: userId,
            gems: gems,
            conf: roomConf,
        };
        reqData.sign = crypto.md5(userId + roomConf + gems + config.ROOM_PRI_KEY);
        const data = await http.get(serverInfo.ip, serverInfo.httpPort, httpRouterMap.CREATE_ROOM, reqData);
        if(data){     
            if(data.errcode == 0){
                return {errcode: 0, roomId: data.roomId};
            }
            else{
                return { errcode: data.errcode, errmsg: data.errmsg }; 
            }
        }
        else{
            return {errcode: 404, errmsg: callPth + callPth + 'createRoom->请求游戏服务器创建房间没有返回数据'};
        }
    }

    /**
     * 进入房间
     */
    static async enterRoom(userId, name, roomId){
        const reqData : ret = {
            userId: userId,
            name: name,
            roomId: roomId,
        };

        reqData.sign = crypto.md5(userId + name + roomId + config.ROOM_PRI_KEY);


        const data = await db.get_room_addr(roomId);
        if(!data){
            return {
                errcode: -2,
                errmsg: callPth + 'enterRoom()->查询运行房间的服务器的IP跟PORT是没有查到或查询出错！',
            };
        }

        const id = data.ip + ':' + data.port;
        const serverInfo = serverMap[id];
        if(serverInfo == null){
            let ret = await this.chooseServerAndEnter(reqData);
            ret.errmsg = callPth + 'enterRoom()->' + ret.errmsg;
            return ret;
        }

        const ret1 = await this.checkRoomIsRuning(serverInfo, roomId);
        if(ret1.errcode != 0){
            let ret = await this.chooseServerAndEnter(reqData);
            ret.errmsg = callPth + 'enterRoom()->' + ret.errmsg;
            return ret;
        }
        
        const ret2 = await this.enterRoomReq(serverInfo,reqData);
        if(ret2.errcode != 0){
            ret2.errmsg = callPth + 'enterRoom()->' + ret2.errmsg;
            return ret2;
        }
        return ret2;
    }


    /**
     * 选择一个负载低的服务器并进入房间
     */
    static async chooseServerAndEnter(reqData: ret){
        const serverInfo = chooseServer();
        if(serverInfo == null){
            return {errcode: -999, errmsg: '所有服务器都爆满了！'};
        }

        const data = await this.enterRoomReq(serverInfo, reqData);
        return data;
    }



    static async enterRoomReq(serverInfo: gameServerInfoType, reqData: ret){
        const ret = await Http.get(serverInfo.ip, serverInfo.httpPort, httpRouterMap.ENTER_ROOM, reqData);
        if(!ret){
            return {
                errcode: -10,
                errmsg: 'enterRoomReq()->错误信息：请求游戏服务器进入房间时没有返回消息',
            };
        }

        if(ret.errcode != 0){
            return ret;
        }

        const dataArr = await Db.set_room_id_of_user(reqData.userId, reqData.roomId);
        return {
            errcode: 0,
            errmsg: 'ok',
            ip: serverInfo.clientIp,
            port: serverInfo.clientPort,
            token: ret.token,
        };
    }


    /**
     * 检查房间是否运行中
     */
    static async checkRoomIsRuning(serverinfo: gameServerInfoType, roomId){
        const sign = Crypto.md5(roomId + config.ROOM_PRI_KEY);
        const ret = await Http.get(serverinfo.ip, serverinfo.httpPort, httpRouterMap.IS_ROOM_RUNING, {roomId: roomId, sign: sign});
        if(!ret){
            return {
                errcode: 18,
                errmsg: 'checkRoomIsRuning()->请求验证房间是否在运行中没有返回数据',
            };
        }

        if(ret.errcode != 0){
            ret.errmsg = 'checkRoomIsRuning()->' + ret.errmsg;
        }
        return ret;
    }
}














router.get(httpRouterMap.RESISTER_GS,       register_gs);
















/**
 * 监听所有游戏服务器发来的消息(更新每个游戏服务器的使用率)
 * @param ctx 
 */
async function register_gs(ctx: Router.IRouterContext){
    const ip = ctx.request.ip;
    const clientIp = ctx.query.clientIp;
    const clientPort = ctx.query.clientPort;
    const httpPort = ctx.query.httpPort;
    const load = ctx.query.load;
    const id = clientIp + ':' + clientPort;

    /*****************************************************************/
    /* 如果要做一个分布服务器的实时监控的GUI界面，所有的数据就是在这里拿到 */
    /*****************************************************************/

    if(serverMap[id]){
        const info = serverMap[id];
        
        if(info.clientPort != clientPort || info.httpPort != httpPort || info.ip != ip){
            // 暂时没搞懂这里什么时候会走，感觉永远不会走这里啊、
            http.send(ctx, 1, id);
            return;
        }

        info.load = load;
        http.send(ctx, 0, 'ok', {ip});
        return;
    }

    serverMap[id] = {ip, id, clientIp, clientPort, httpPort, load};
    http.send(ctx, 0, 'ok', {ip});
    
    console.log(`有一个游戏服务器注册了：
        id:${id}
        地址:${ip}
        http端口:${httpPort}
        socket端口:${clientPort}`);
}





/**
 * 选择一个负载最少的游戏服务器
 */
function chooseServer(){
    let serverInfo: gameServerInfoType = null;
    for(let s in serverMap){
        let info = serverMap[s];

        if(serverInfo == null){
            serverInfo = info;
        }
        else{
            if(serverInfo.load > info.load){
                serverInfo = info;
            }
        }
    }
    return serverInfo;
}